home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d16 / winvn060.arc / WVBLOCK.C < prev    next >
C/C++ Source or Header  |  1991-07-01  |  46KB  |  1,288 lines

  1. /* -- WVBLOCK.C --------------------------------------------------
  2.  *
  3.  *  This file contains a collection of routines to manipulate textblocks
  4.  *  and lines within textblocks.
  5.  *  The routines here view lines as atomic units only (they don't
  6.  *  look at the actual data in the lines).
  7.  *
  8.  *  Mark Riordan   20 September 1989.
  9.  */
  10.  
  11. #include "windows.h"
  12. #include "wvglob.h"
  13. #include "winvn.h"
  14. #include <ctype.h>
  15.  
  16. /*-- function NewBlock ------------------------------------------
  17.  *
  18.  *  Creates an empty, new textblock and links it into the list
  19.  *  of blocks after a given block.
  20.  *  After the call, both the old ("given") block and the new block
  21.  *  are locked in memory.
  22.  *
  23.  *    Entry    CurBlockPtr    points to a block
  24.  *
  25.  *    Exit     NewBlockPtr    points to a block that has been linked
  26.  *                            into the list just after CurBlockPtr.
  27.  *             Returns TRUE if couldn't allocate a block, else FALSE.
  28.  *             (I know I should fix that.)
  29.  */
  30. int
  31. NewBlock(CurBlockPtr,NewBlockPtr)
  32. TypBlock far *CurBlockPtr, far **NewBlockPtr;
  33. {
  34.    HANDLE hMyBlock;
  35.    TypBlock far *MyBlock, far *MyNextBlock;
  36.  
  37.    hMyBlock = GlobalAlloc(GMEM_MOVEABLE,(long)(CurBlockPtr->OwnerDoc->BlockSize));
  38.    if(hMyBlock) {
  39.       MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
  40.       SetupEmptyBlock(MyBlock,hMyBlock,CurBlockPtr->hCurBlock,
  41.        CurBlockPtr->hNextBlock,CurBlockPtr->OwnerDoc);
  42.       CurBlockPtr->hNextBlock = hMyBlock;
  43.  
  44.       /* Change the next block's "previous" pointer to point to us. */
  45.  
  46.       if(MyBlock->hNextBlock) {
  47.          MyNextBlock = (TypBlock far *) GlobalLock(MyBlock->hNextBlock);
  48.          MyNextBlock->hPrevBlock = hMyBlock;
  49.          GlobalUnlock(MyBlock->hNextBlock);
  50.       }
  51.  
  52.       *NewBlockPtr = MyBlock;
  53.    } else {
  54.       MessageBox(CurBlockPtr->OwnerDoc->hDocWnd,"Could not allocate textblock","Out of Memory Error",MB_OK|MB_ICONHAND);
  55.       return(1);
  56.    }
  57.    return(0);
  58. }
  59.  
  60. /*-- function SetupEmptyBlock -----------------------------------------
  61.  *
  62.  *  Initialize fields in a newly-allocated textblock.
  63.  *  Set the fields to indicate an empty block.
  64.  */
  65. void
  66. SetupEmptyBlock(BlockPtr,hCur,hPrev,hNext,DocPtr)
  67. TypBlock far *BlockPtr;
  68. HANDLE hCur, hPrev, hNext;
  69. TypDoc *DocPtr;
  70. {
  71.    BlockPtr->OwnerDoc   = DocPtr;
  72.    BlockPtr->hCurBlock  = hCur;
  73.    BlockPtr->hPrevBlock = hPrev;
  74.    BlockPtr->hNextBlock = hNext;
  75.    BlockPtr->LWAp1      = sizeof(TypBlock) + sizeof(TypLine);
  76.    BlockPtr->NumLines   = 0;
  77.    BlockPtr->eob        = END_OF_BLOCK;
  78. #if 0
  79.    *((int far *) ((char far *)BlockPtr+sizeof(TypBlock)) ) = END_OF_BLOCK;
  80. #else
  81.    ((TypLine far *) ((char far *)BlockPtr+sizeof(TypBlock)) )->length = END_OF_BLOCK;
  82.    ((TypLine far *) ((char far *)BlockPtr+sizeof(TypBlock)) )->LineID = NextLineID++;
  83. #endif
  84. }
  85.  
  86. /*-- function DeleteBlock ---------------------------------------------
  87.  *
  88.  *  Delete a textblock from a document.
  89.  *
  90.  *    Entry    CurBlockPtr points to the block to delete.
  91.  *
  92.  *    Exit     CurBlockPtr points to the next block, if any, else the
  93.  *                         previous block.
  94.  *             CurLinePtr  points to the first line of the next block
  95.  *                         if there was one, else the last line of
  96.  *                         the previous block.
  97.  */
  98. BOOL
  99. DeleteBlock(TypBlock far **CurBlockPtr, TypLine far **CurLinePtr)
  100. {
  101.    TypBlock far *MyBlockPtr = *CurBlockPtr;
  102.    TypBlock far *BlockPtr;
  103.    HANDLE hMyBlock = MyBlockPtr->hCurBlock,
  104.           hMyPrev  = MyBlockPtr->hPrevBlock,
  105.           hMyNext  = MyBlockPtr->hNextBlock;
  106.    BOOL set_cur_block = FALSE;
  107.  
  108.    /* Don't delete the only block in the document. */
  109.    if(!hMyNext && !hMyPrev) return(FALSE);
  110.  
  111.    /* Update the prev pointer of the next block to point to
  112.     * the block previous to the one being deleted.
  113.     */
  114.  
  115.    if(hMyNext) {
  116.       BlockPtr = (TypBlock far *) GlobalLock(hMyNext);
  117.       BlockPtr->hPrevBlock = hMyPrev;
  118.       GlobalUnlock(hMyNext);
  119.  
  120.       NextLine(CurBlockPtr,CurLinePtr);
  121.       set_cur_block = TRUE;
  122.    } else {
  123.       /* The block we are deleting has no next block, so it
  124.        * must be the last in the document.  Update document pointers.
  125.        */
  126.       MyBlockPtr->OwnerDoc->hLastBlock = hMyPrev;
  127.    }
  128.  
  129.    /* Update the next pointer of the previous block to point to
  130.     * the block after the one being deleted.
  131.     */
  132.  
  133.    if(hMyPrev) {
  134.       BlockPtr = (TypBlock far *) GlobalLock(hMyPrev);
  135.       BlockPtr->hNextBlock = hMyNext;
  136.       GlobalUnlock(hMyPrev);
  137.  
  138.       if(!set_cur_block) {
  139.          /* There is no next block, so we want to position the
  140.           * pointer to the end of the previous block.
  141.           */
  142.          PrevLine(CurBlockPtr,CurLinePtr);
  143.          NextLine(CurBlockPtr,CurLinePtr);
  144.       }
  145.    } else {
  146.       /* The block we are deleting has no previous block, so it
  147.        * must be the first in the document.  Update document pointers.
  148.        */
  149.       MyBlockPtr->OwnerDoc->hFirstBlock = hMyNext;
  150.    }
  151.  
  152.    GlobalFree(hMyBlock);
  153.    return(TRUE);
  154. }
  155.  
  156. /*-- function AddLine -------------------------------------------------
  157.  *
  158.  *  Add a line to a textblock.  Create a new textblock if necessary.
  159.  *
  160.  *    Entry    LineToAdd   points to a line to add.
  161.  *             CurBlockPtr points to the block to which we want to add it.
  162.  *             CurAddPtr   points to the place in the block to add the line.
  163.  *
  164.  *    Exit     CurBlockPtr & CurAddPtr point to right after the
  165.  *                newly-added line.
  166.  *
  167.  *    Method   There are three cases:
  168.  *             1. The new line will fit in the textblock.
  169.  *                That's pretty easy.
  170.  *             2. The new line won't fit, and we are at the end of
  171.  *                the current block.
  172.  *                I could have just treated this as I do case 3 (splitting
  173.  *                the textblock), but that would have resulted in
  174.  *                a lot of unnecessary internal fragmentation
  175.  *                (i.e., wasted space inside textblocks).
  176.  *                So, what I do is leave the contents of the old block
  177.  *                alone and just create a new empty block, to which
  178.  *                the line is added by a recursive call.
  179.  *             3. The new line won't fit, and we are not at the end of
  180.  *                the textblock.
  181.  *                In this case, I split the textblock at the first line
  182.  *                boundary after the split point (the split point is
  183.  *                an attribute of the document--usually about 2/3 of the
  184.  *                way through the textblock), creating two semi-full textblocks
  185.  *                where there was one full one before.
  186.  *                The routine then recursively calls itself.
  187.  */
  188. int
  189. AddLine(LineToAdd,CurBlockPtr,CurAddPtr)
  190. TypLine *LineToAdd;
  191. TypBlock far **CurBlockPtr;
  192. TypLine far **CurAddPtr;
  193. {
  194.    TypBlock far *MyBlock = *CurBlockPtr;
  195.  
  196.    int left = (MyBlock->OwnerDoc->BlockSize - MyBlock->LWAp1);
  197.  
  198.    if(LineToAdd->length <= ( int)left ) {
  199.  
  200.       /* There's room in the current block for this line, so just    */
  201.       /* move lines down to accomodate this line and copy it in.     */
  202.  
  203.       MoveBytes((char far *) *CurAddPtr,
  204.        ((char far *) *CurAddPtr)+LineToAdd->length,
  205.        (((char far *)MyBlock+MyBlock->LWAp1)) - (char far *) *CurAddPtr);
  206.       MoveBytes((char far *)LineToAdd,(char far *) *CurAddPtr,
  207.        LineToAdd->length);
  208.  
  209.       /* Adjust textblock counters.                                  */
  210.       MyBlock->LWAp1 += LineToAdd->length;
  211.       MyBlock->NumLines++;
  212.       IncPtr((*CurAddPtr),LineToAdd->length);
  213.       MyBlock->OwnerDoc->TotalLines++;
  214.    } else {
  215.  
  216.       /* There isn't enough room in the current textblock.           */
  217.       /* If we are at the end of the current block, just create      */
  218.       /* a new empty one; otherwise, split the current block.        */
  219.  
  220.       TypBlock far *NewBlockPtr;
  221.  
  222.       if((*CurAddPtr)->length == END_OF_BLOCK) {
  223.          /* We're at end of block; create a new empty one.           */
  224.          /* This will be the current block, so release references    */
  225.          /* to the now-current block                                 */
  226.          if(NewBlock(MyBlock,CurBlockPtr)) {
  227.             return(1);
  228.          } else {
  229.             GlobalUnlock(MyBlock->hCurBlock);
  230.             *CurAddPtr = (TypLine far *)
  231.              ((char far *)*CurBlockPtr + sizeof(TypBlock));
  232.             /* Now we can recursively try again to add the line.     */
  233.             if(AddLine(LineToAdd,CurBlockPtr,CurAddPtr)) {
  234.                return(1);
  235.             }
  236.          }
  237.       } else {
  238.          /* We need to split the textblock.                          */
  239.          /* Find a place to split the block by starting at the       */
  240.          /* beginning of the block and skipping through the lines    */
  241.          /* until we pass the number of bytes that marks the         */
  242.          /* split point.  The previous line is the split point.      */
  243.  
  244.          TypLine far *MyLinePtr = (TypLine far *)((char far *)MyBlock+sizeof(TypBlock));
  245.          TypLine far *MyLastLine = MyLinePtr;
  246.          int nOldLines = 0, nBytesMoved, MyAddOffset;
  247.  
  248.          while((char far *)MyLinePtr - (char far *) MyBlock < MyBlock->OwnerDoc->SplitSize &&
  249.           MyLinePtr->length != END_OF_BLOCK) {
  250.             nOldLines++;
  251.             MyLastLine = MyLinePtr;
  252.             IncPtr(MyLinePtr,MyLinePtr->length);
  253.          }
  254.  
  255.          /* Allocate the new block and copy the last portion of      */
  256.          /* the current block to the new one.   The range to         */
  257.          /* copy starts at the above-determined split point and      */
  258.          /* goes until the LWA+1.                                    */
  259.          /* Then adjust the new & old textblock fields.              */
  260.  
  261.          if(NewBlock(MyBlock,&NewBlockPtr)) {
  262.             return(1);
  263.          } else {
  264.             MoveBytes(MyLastLine,(char far *)NewBlockPtr+sizeof(TypBlock),
  265.              nBytesMoved = (((char far *)MyBlock + MyBlock->LWAp1)) - (char far *)MyLastLine);
  266.             MyBlock->LWAp1 = (char far *)MyLastLine - ((char far *)MyBlock)
  267.              + sizeof(TypLine);
  268.             ((TypLine far *)MyLastLine)->length = END_OF_BLOCK;
  269.             ((TypLine far *)MyLastLine)->LineID = NextLineID++;
  270.             NewBlockPtr->NumLines = MyBlock->NumLines - nOldLines;
  271.             MyBlock->NumLines = nOldLines;
  272.             NewBlockPtr->LWAp1 = nBytesMoved + sizeof(TypBlock);
  273.  
  274.             /* Should the new line go in the old block or the new?   */
  275.             /* If the add point is beyond the end of the newly-      */
  276.             /* truncated block, we must move the add point to the    */
  277.             /* next block and make the new block the current one.    */
  278.             /* The new position should be the same                   */
  279.             /* number of line bytes past the beginning of the next   */
  280.             /* block as it was past the split point when it was in   */
  281.             /* the old block.                                        */
  282.             /* Either way, one block (the old or the new one)        */
  283.             /* must be unlocked.                                     */
  284.  
  285.             if(*CurAddPtr >= MyLastLine) {
  286.                /* Add point is in new block.                         */
  287.  
  288.                MyAddOffset = (char far *) *CurAddPtr - (char far *)MyLastLine + sizeof(TypBlock);
  289.                *CurAddPtr = (TypLine far *)((char far *)NewBlockPtr + MyAddOffset);
  290.                *CurBlockPtr = NewBlockPtr;
  291.                GlobalUnlock(MyBlock->hCurBlock);
  292.             } else {
  293.                /* Add point is in current block.                     */
  294.                GlobalUnlock(NewBlockPtr->hCurBlock);
  295.             }
  296.             return(AddLine(LineToAdd,CurBlockPtr,CurAddPtr));
  297.          }
  298.       }
  299.    }
  300.    return(0);
  301. }
  302.  
  303. /*-- function ReplaceLine -------------------------------------------------
  304.  *
  305.  *  Replace a line in a textblock.  Create a new textblock if necessary.
  306.  *
  307.  *    Entry    LineToAdd   is the line to put into a textblock.
  308.  *             CurBlockPtr points to the block containing the old copy.
  309.  *             CurLinePtr  points to the old copy of the line.
  310.  *
  311.  *    Exit     returns TRUE if successful.
  312.  *             CurBlockPtr points to the block containing the new copy of the line.
  313.  *             CurLinePtr  points to the new copy of the line.
  314.  *                         Usually, CurBlockPtr & CurAddPtr will be the
  315.  *                         same upon exit as upon entry; however, sometimes
  316.  *                         a textblock split is necessary.
  317.  *
  318.  *    Method   There are two cases:
  319.  *             1. There is enough room in this textblock for the
  320.  *                changed line.
  321.  *                This is pretty simple.
  322.  *             2. There is not enough room in this textblock for the
  323.  *                changed line.  This requires a textblock split,
  324.  *                as with AddLine.  There's a lot of common code
  325.  *                here--should probably consolidate it in a single routine
  326.  *                some day.
  327.  */
  328. BOOL
  329. ReplaceLine(LineToAdd,CurBlockPtr,CurLinePtr)
  330. TypLine *LineToAdd;
  331. TypBlock far **CurBlockPtr;
  332. TypLine far **CurLinePtr;
  333. {
  334.    TypBlock far *MyBlockPtr = *CurBlockPtr;
  335.    TypLine  far *MyLinePtr  = *CurLinePtr;
  336.    int deltasize;
  337.    int numbytes;
  338.    char far *target, far *source;
  339.  
  340.    deltasize = LineToAdd->length - (MyLinePtr->length);
  341.  
  342.    if(deltasize <= ( int)(MyBlockPtr->OwnerDoc->BlockSize - MyBlockPtr->LWAp1) ) {
  343.  
  344.       /* There's room in the current block for this line, so just    */
  345.       /* move lines down to accomodate this line and copy it in.     */
  346.       /* Move the data in the textblock up or down, starting with    */
  347.       /* the line after the line being replaced.                     */
  348.  
  349.       source = (char far *)MyLinePtr + MyLinePtr->length;
  350.       target = source + deltasize;
  351.       numbytes = ((char far *)MyBlockPtr+MyBlockPtr->LWAp1) -
  352.         (char far *)MyLinePtr - MyLinePtr->length;
  353. #if 0
  354.       numbytes = (char far *)MyBlockPtr+MyBlockPtr->LWAp1 - (char far *)MyLinePtr;
  355.       target = (char far *)MyLinePtr + deltasize;
  356. #endif
  357.       MoveBytes(source,target,numbytes);
  358.  
  359.       MoveBytes((char far *)LineToAdd,(char far *) MyLinePtr,
  360.        LineToAdd->length);
  361.       MyBlockPtr->LWAp1 += deltasize;
  362.    } else {
  363.  
  364.       /* There isn't enough room in the current textblock.           */
  365.       /* We need to split the textblock.                          */
  366.       /* Find a place to split the block by starting at the       */
  367.       /* beginning of the block and skipping through the lines    */
  368.       /* until we pass the number of bytes that marks the         */
  369.       /* split point.  The previous line is the split point.      */
  370.  
  371.       TypBlock far *NewBlockPtr;
  372.  
  373.       TypLine far *MyLinePtr = (TypLine far *)((char far *)MyBlockPtr+sizeof(TypBlock));
  374.       TypLine far *MyLastLine = MyLinePtr;
  375.       int nOldLines = 0, nBytesMoved, MyAddOffset;
  376.  
  377. #if 0
  378.       MessageBox(hWndConf,"Need to split textblock","in ReplaceLine",MB_OK);
  379. #endif
  380.  
  381.       while(((char far *)MyLinePtr - (char far *) MyBlockPtr) < MyBlockPtr->OwnerDoc->SplitSize &&
  382.        MyLinePtr->length != END_OF_BLOCK) {
  383.          nOldLines++;
  384.          MyLastLine = MyLinePtr;
  385. /*         (char far *) MyLinePtr += MyLinePtr->length; */
  386.          IncPtr(MyLinePtr,MyLinePtr->length);
  387.       }
  388.  
  389.       /* Allocate the new block and copy the last portion of      */
  390.       /* the current block to the new one.   The range to         */
  391.       /* copy starts at the above-determined split point and      */
  392.       /* goes until the LWA+1.                                    */
  393.       /* Then adjust the new & old textblock fields.              */
  394.  
  395.       if(NewBlock(MyBlockPtr,&NewBlockPtr)) {
  396.          return(1);
  397.       } else {
  398.          MoveBytes(MyLastLine,(char far *)NewBlockPtr+sizeof(TypBlock),
  399.           nBytesMoved = (((char far *)MyBlockPtr + MyBlockPtr->LWAp1)) - (char far *)MyLastLine);
  400.          MyBlockPtr->LWAp1 = (char far *)MyLastLine - ((char far *)MyBlockPtr)
  401.           + sizeof(TypLine);
  402.          ((TypLine far *)MyLastLine)->length = END_OF_BLOCK;
  403.          ((TypLine far *)MyLastLine)->LineID = NextLineID++;
  404.          NewBlockPtr->NumLines = MyBlockPtr->NumLines - nOldLines;
  405.          MyBlockPtr->NumLines = nOldLines;
  406.          NewBlockPtr->LWAp1 = nBytesMoved + sizeof(TypBlock);
  407.  
  408.          /* Should this line go in the old block or the new?   */
  409.          /* If the add point is beyond the end of the newly-      */
  410.          /* truncated block, we must move the add point to the    */
  411.          /* next block and make the new block the current one.    */
  412.          /* The new position should be the same                   */
  413.          /* number of line bytes past the beginning of the next   */
  414.          /* block as it was past the split point when it was in   */
  415.          /* the old block.                                        */
  416.          /* Either way, one block (the old or the new one)        */
  417.          /* must be unlocked.                                     */
  418.  
  419.          if(*CurLinePtr >= MyLastLine) {
  420.             /* Replace point is in new block.                         */
  421.  
  422.             MyAddOffset = (char far *) *CurLinePtr - (char far *)MyLastLine + sizeof(TypBlock);
  423.             *CurLinePtr = (TypLine far *)((char far *)NewBlockPtr + MyAddOffset);
  424.             *CurBlockPtr = NewBlockPtr;
  425.             GlobalUnlock(MyBlockPtr->hCurBlock);
  426.          } else {
  427.             /* Add point is in current block.                     */
  428.             GlobalUnlock(NewBlockPtr->hCurBlock);
  429.          }
  430.          return(ReplaceLine(LineToAdd,CurBlockPtr,CurLinePtr));
  431.       }
  432.    }
  433.    return(TRUE);
  434. }
  435.  
  436. /*-- function DeleteLine ----------------------------------------------
  437.  *
  438.  *  Delete a line from a textblock.
  439.  *
  440.  *    Entry    CurBlockPtr points to the block containing the line.
  441.  *             CurLinePtr  points to the line to delete.
  442.  *
  443.  *    Exit     returns TRUE if successful.
  444.  *             CurBlockPtr points to the block containing the next line,
  445.  *                         if any.
  446.  *             CurLinePtr  points to the next line, if any--else the
  447.  *                         end of the block.
  448.  *                         Usually, CurBlockPtr & CurLinePtr will be the
  449.  *                         same upon exit as upon entry; however, sometimes
  450.  *                         a textblock is emptied and the entire block
  451.  *                         is deleted.
  452.  */
  453. BOOL
  454. DeleteLine(TypBlock far **CurBlockPtr, TypLine far **CurLinePtr)
  455. {
  456.    TypBlock far *MyBlockPtr = *CurBlockPtr;
  457.    TypLine  far *MyLinePtr  = *CurLinePtr;
  458.    int bytes_to_end, bytes_to_copy;
  459.    int cur_length = MyLinePtr->length;
  460.  
  461.    /* If we are (erroneously) at the end of a block, do nothing */
  462.  
  463.    if(MyLinePtr->length == END_OF_BLOCK) {
  464.       return(FALSE);
  465.    }
  466.  
  467.    /* Copy the remainder of the block on top of the line to be deleted. */
  468.  
  469.    bytes_to_end = ((char far *)MyBlockPtr+MyBlockPtr->LWAp1) -
  470.      (char far *) MyLinePtr;
  471.    bytes_to_copy = bytes_to_end - cur_length;
  472.    MoveBytes((char far *)MyLinePtr+cur_length, (char far *)MyLinePtr,
  473.      bytes_to_copy);
  474.  
  475.    /* Update the block counters.  */
  476.  
  477.    MyBlockPtr->LWAp1 -= cur_length;
  478.    MyBlockPtr->NumLines--;
  479.    MyBlockPtr->OwnerDoc->TotalLines--;
  480.  
  481.    /* If we are now at the end of the block, we are faced with one of
  482.     * two situations:
  483.     * 1.  We are also at the beginning of the block, and hence
  484.     *     the block is empty.  In this case we must delete the block
  485.     *     unless it is the only block.
  486.     * 2.  Otherwise, we must advance to the next block, if any.
  487.     */
  488.  
  489.    if(MyLinePtr->length == END_OF_BLOCK) {
  490.  
  491.       /* We are at the end of the block.  */
  492.  
  493.       if( *((int far *)(MyLinePtr)-1) == END_OF_BLOCK) {
  494.  
  495.          /* We have emptied the block.  We must check for whether
  496.           * this is the last block in the document.
  497.           */
  498.          if(MyBlockPtr->OwnerDoc->TotalLines) {
  499.             /* The document is not empty.  Delete this empty block.
  500.              */
  501.             DeleteBlock(CurBlockPtr,CurLinePtr);
  502.          } else {
  503.             /* The document is empty.  Don't delete this block.
  504.              * Leave the pointer at the same place.  It is now pointing
  505.              * at the next line.
  506.              */
  507.          }
  508.       } else {
  509.          /* We're at the end of the block, but the block is not empty.
  510.           * Just advance to the next block, if any.
  511.           */
  512.          NextLine(CurBlockPtr,CurLinePtr);
  513.       }
  514.    }
  515.  
  516.    return(TRUE);
  517. }
  518.  
  519.  
  520. /*-- function NextLine ------------------------------------------------
  521.  *
  522.  *  Advance a pointer to point to the next line in a document.
  523.  *
  524.  *    Entry    BlockPtr    points to the current block.
  525.  *             LinePtr     points to the current line.
  526.  *
  527.  *    Exit     BlockPtr & LinePtr point to the next line, if there
  528.  *              was one.
  529.  *             Returns TRUE iff pointer was moved.
  530.  *
  531.  *    Method   Must advance BlockPtr if LinePtr was at the end
  532.  *             of a block to start with, or arrives at the end of a block
  533.  *             after moving to the end of the current line.
  534.  */
  535. int
  536. NextLine(BlockPtr,LinePtr)
  537. TypBlock far **BlockPtr;
  538. TypLine far **LinePtr;
  539. {
  540.    BOOL retcode = 0;
  541.  
  542.    if((*LinePtr)->length != END_OF_BLOCK) {
  543. /*      (char far *) *LinePtr += (*LinePtr)->length; */
  544.       IncPtr(*LinePtr,(*LinePtr)->length);
  545.    }
  546.    if((*LinePtr)->length == END_OF_BLOCK) {
  547.       if( (*BlockPtr)->hNextBlock) {
  548.          GlobalUnlock( (*BlockPtr)->hCurBlock);
  549.          *BlockPtr = (TypBlock far *) GlobalLock( (*BlockPtr)->hNextBlock);
  550.          *LinePtr = (TypLine far *)((char far *)*BlockPtr + sizeof(TypBlock));
  551.          retcode = 1;
  552.       }
  553.    } else {
  554.       retcode = 1;
  555.    }
  556.    return(retcode);
  557. }
  558.  
  559. /*-- function PrevLine ------------------------------------------------
  560.  *
  561.  *  Back up a pointer to point to the next line in a document.
  562.  *
  563.  *    Entry    BlockPtr    points to the current block.
  564.  *             LinePtr     points to the current line.
  565.  *
  566.  *    Exit     BlockPtr & LinePtr point to the previous line, if there
  567.  *              was one.
  568.  *             Returns TRUE iff pointer was moved.
  569.  *
  570.  *    Method   Rely on the fact that the last field of the previous
  571.  *             line is the length of that line.  Also, the last field
  572.  *             in a textblock header (which is what you get if you
  573.  *             try to look at the previous line in a textblock if you're
  574.  *             at the beginning of a textblock) has the value END_OF_BLOCK.
  575.  */
  576. int
  577. PrevLine(BlockPtr,LinePtr)
  578. TypBlock far **BlockPtr;
  579. TypLine far **LinePtr;
  580. {
  581.    if( *((int far *)(*LinePtr)-1) != END_OF_BLOCK) {
  582. /*      (char far *) *LinePtr -= *((int far *)(*LinePtr)-1); */
  583.       IncPtr(*LinePtr,-(*((int far *)(*LinePtr)-1)));
  584.    } else {
  585.       if( (*BlockPtr)->hPrevBlock) {
  586.          GlobalUnlock( (*BlockPtr)->hCurBlock);
  587.          *BlockPtr = (TypBlock far *) GlobalLock( (*BlockPtr)->hPrevBlock);
  588.          *LinePtr = (TypLine far *)
  589.           ( (char far *)*BlockPtr + (*BlockPtr)->LWAp1 - sizeof(TypLine) );
  590. /*         (char far *) *LinePtr -= ( *((int far *)(*LinePtr)-1)); */
  591.          IncPtr(*LinePtr,-( *((int far *)(*LinePtr)-1)) );
  592.       } else {
  593.          return(0);
  594.       }
  595.    }
  596.    return(1);
  597. }
  598.  
  599. /*--- function TopOfDoc ------------------------------------------------
  600.  *
  601.  *   Set pointers to the first line of a document.
  602.  *
  603.  *    Entry    Doc         points to a document.
  604.  *
  605.  *    Exit     BlockPtr, LinePtr   point to the first
  606.  *                         line in the document.  This line is locked.
  607.  */
  608. void
  609. TopOfDoc(Doc,BlockPtr,LinePtr)
  610. TypDoc *Doc;
  611. TypBlock far **BlockPtr;
  612. TypLine far **LinePtr;
  613. {
  614.    HANDLE hBlock;
  615.    int Offset;
  616.    TypLineID MyLineID;
  617.  
  618.    hBlock = Doc->hFirstBlock;
  619.    Offset = sizeof(TypBlock);
  620.    MyLineID = 0L;
  621.    LockLine(hBlock,Offset,MyLineID,BlockPtr,LinePtr);
  622. }
  623.  
  624. /*--- function ExtractTextLine -----------------------------------------------
  625.  *
  626.  *  Extract the text portion of a line to another buffer.
  627.  *
  628.  *    Entry    Doc      points to the document.
  629.  *             LinePtr  points to a line.
  630.  *             BufSize  is the size of the output buffer in bytes.
  631.  *
  632.  *    Exit     Buf      contains the text, terminated by a zero byte.
  633.  *             Returns FALSE iff no text could be extracted
  634.  *               (not a valid line).
  635.  */
  636. BOOL
  637. ExtractTextLine(Doc,LinePtr,Buf,BufSize)
  638. TypDoc *Doc;
  639. TypLine far *LinePtr;
  640. char *Buf;
  641. int  BufSize;
  642. {
  643.    char far *bptr;
  644.    BOOL DidIt = FALSE;
  645.  
  646.    if(LinePtr->length == END_OF_BLOCK) {
  647.    } else {
  648.       if(Doc->DocType == DOCTYPE_NET) {
  649.          bptr = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);
  650.          while(--BufSize > 1 && (*(Buf++) = *(bptr++)));
  651.          *Buf = '\0';
  652.          DidIt = TRUE;
  653.       } else if(Doc->DocType == DOCTYPE_ARTICLE) {
  654.          bptr = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypText);
  655.          while(--BufSize > 1 && (*(Buf++) = *(bptr++)));
  656.          *Buf = '\0';
  657.          DidIt = TRUE;
  658.       }
  659.    }
  660.    return (DidIt);
  661. }
  662.  
  663. /*-- function LockLine ---------------------------------------------
  664.  *
  665.  *  Find the specified line, and return a pointer to it.
  666.  *  Lock the line in memory.
  667.  *
  668.  *  Entry:  hBlock      is the handle of a block we think contains
  669.  *                      the desired line.
  670.  *          LineOff     is the offset in bytes from the beginning of
  671.  *                      the block for where we think the line is.
  672.  *          FindLineID  is the LineID of the desired line.
  673.  *                      If it is 0, we don't check line ID's (don't care).
  674.  *
  675.  *  Exit:   returns     TRUE iff the line was found.
  676.  *          BlockPtr    points to the beginning of the block
  677.  *                      in which the line was found.
  678.  *          LinePtr     points to the line.
  679.  */
  680. BOOL
  681. LockLine(hBlock,LineOff,FindLineID,BlockPtr,LinePtr)
  682. HANDLE hBlock;
  683. unsigned int LineOff;
  684. TypLineID FindLineID;
  685. TypBlock far **BlockPtr;
  686. TypLine far **LinePtr;
  687. {
  688.    *BlockPtr = (TypBlock far *) GlobalLock(hBlock);
  689.    *LinePtr = (TypLine far *) ((char far *)*BlockPtr + LineOff);
  690.    if(FindLineID && (*LinePtr)->LineID != FindLineID) {
  691.       /* The location specified by hBlock and LineOff does not
  692.        * contain the right line.  So, unlock that block and start
  693.        * scanning the document from the top, looking for the line.
  694.        */
  695.       TypBlock far *MyBlockPtr;
  696.       TypLine  far *MyLinePtr;
  697.       HANDLE hMyBlock;
  698.       int    MyOffset;
  699.  
  700. #if 0
  701.       MessageBox(hWndConf,"LineID doesn't match","in LockLine",MB_OK);
  702. #endif
  703.  
  704.       hMyBlock = (*BlockPtr)->OwnerDoc->hFirstBlock;
  705.       MyOffset = sizeof(TypBlock);
  706.       GlobalUnlock(hBlock);
  707.  
  708.       MyBlockPtr = (TypBlock far *)GlobalLock(hMyBlock);
  709.       MyLinePtr  = (TypLine far *) ((char far *)MyBlockPtr + sizeof(TypBlock));
  710.  
  711.       while(MyLinePtr->LineID != FindLineID && NextLine(&MyBlockPtr,&MyLinePtr));
  712.  
  713.       if(MyLinePtr->LineID == FindLineID) {
  714.          *BlockPtr = MyBlockPtr;
  715.          *LinePtr  = MyLinePtr;
  716.       } else {
  717.          MessageBox(hWndConf,"Can't find line","in LockLine",MB_ICONHAND|MB_OK);
  718.          return(FALSE);
  719.       }
  720.    }
  721.    return(TRUE);
  722. }
  723.  
  724. /*-- function UnlockLine ---------------------------------------------
  725.  *
  726.  *  Given a block pointer and a line pointer, unlock the block
  727.  *  in memory and return a line ID, a block handle and an offset within the
  728.  *  block to the line.
  729.  *
  730.  *    Entry    BlockPtr    points to a textblock
  731.  *             LinePtr     points to a line in that textblock
  732.  *
  733.  *    Exit     TheLineID   is the LineID of the pointed-to line.
  734.  *             hBlock      is the handle to the textblock.
  735.  *             LineOff     is the offset (in bytes from the beginning
  736.  *                         of the block) of the line.
  737.  */
  738. void
  739. UnlockLine(BlockPtr,LinePtr,hBlock,LineOff,TheLineID)
  740. TypBlock far *BlockPtr;
  741. TypLine far *LinePtr;
  742. HANDLE *hBlock;
  743. unsigned int *LineOff;
  744. TypLineID *TheLineID;
  745. {
  746.    PtrToOffset(BlockPtr,LinePtr,hBlock,LineOff,TheLineID);
  747.    GlobalUnlock(*hBlock);
  748. }
  749.  
  750. /*-- function PtrToOffset ---------------------------------------------
  751.  *
  752.  *  Given a block pointer and a line pointer,
  753.  *  return a line ID, a block handle and an offset within the
  754.  *  block to the line.
  755.  *
  756.  *    Entry    BlockPtr    points to a textblock
  757.  *             LinePtr     points to a line in that textblock
  758.  *
  759.  *    Exit     TheLineID   is the LineID of the pointed-to line.
  760.  *             hBlock      is the handle to the textblock.
  761.  *             LineOff     is the offset (in bytes from the beginning
  762.  *                         of the block) of the line.
  763.  */
  764. void
  765. PtrToOffset(BlockPtr,LinePtr,hBlock,LineOff,TheLineID)
  766. TypBlock far *BlockPtr;
  767. TypLine far *LinePtr;
  768. HANDLE *hBlock;
  769. unsigned int *LineOff;
  770. TypLineID *TheLineID;
  771. {
  772.    *TheLineID = LinePtr->LineID;
  773.    *LineOff = (char far *)LinePtr - (char far *)BlockPtr;
  774.    *hBlock = BlockPtr->hCurBlock;
  775. }
  776.  
  777. /*-- function WhatLine ------------------------------------------------
  778.  *
  779.  *  Determine the ordinal number of a given line in the document.
  780.  *
  781.  *    Entry    BlockPtr    points to the block containing a line.
  782.  *             LinePtr     points to the line.
  783.  *
  784.  *    Exit     Returns 0 = first line, 1 = second, and so on.
  785.  *
  786.  *  Strategy is to start at the beginning of the document and
  787.  *  scan though the lines, counting lines until we reach the
  788.  *  current line.  In more detail:
  789.  *
  790.  *  Save the current position.
  791.  *  Go to the first block of the document.
  792.  *  Number of lines = 0
  793.  *  While we are not yet at the original block,
  794.  *     Add in the number of lines in this block, just by looking at header.
  795.  *  Now that we have reached the original block, start at the
  796.  *     beginning of the block and scan forward line-by-line until
  797.  *     we reach the original line.
  798.  */
  799. unsigned int
  800. WhatLine(BlockPtr,LinePtr)
  801. TypBlock far *BlockPtr;
  802. TypLine far *LinePtr;
  803. {
  804.    unsigned int nLines = 0;
  805.    TypBlock far *MyBlock;
  806.    TypLine far *MyLine;
  807.    TypDoc *MyDoc;
  808.    HANDLE hOrgBlock, hMyBlock, hNewBlock;
  809.    unsigned int OrgOffset, MyOffset;
  810.    TypLineID OrgLineID, MyLineID=0L;
  811.  
  812.    MyDoc = BlockPtr->OwnerDoc;
  813.    UnlockLine(BlockPtr,LinePtr,&hOrgBlock,&OrgOffset,&OrgLineID);
  814.  
  815.    hMyBlock = MyDoc->hFirstBlock;
  816.    while(hMyBlock != hOrgBlock) {
  817.       MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
  818.       nLines += MyBlock->NumLines;
  819.       hNewBlock = MyBlock->hNextBlock;
  820.       if(!hNewBlock) {
  821.          MessageBox(MyDoc->hDocWnd,"Hit end of document","Error in WhatLine",
  822.           MB_OK | MB_ICONHAND);
  823.       }
  824.       GlobalUnlock(hMyBlock);
  825.       hMyBlock = hNewBlock;
  826.    }
  827.  
  828.    /* The technique of scanning the NumLines field starts the        */
  829.    /* counter at 1 rather than 0, so if we got any lines above,      */
  830.    /* adjust the count by decrementing it.                           */
  831.  
  832. /* if(nLines) nLines--; */
  833.  
  834.    /* We have reached the original block.                            */
  835.    /* Start scanning at the first line.                              */
  836.  
  837.    MyOffset = sizeof(TypBlock);
  838.    LockLine(hMyBlock,MyOffset,MyLineID,&MyBlock,&MyLine);
  839.    while(MyOffset != OrgOffset) {
  840.       nLines++;
  841.       NextLine(&MyBlock,&MyLine);
  842.       MyOffset = (char far *) MyLine - (char far *) MyBlock;
  843.    }
  844.  
  845.    return(nLines);
  846. }
  847.  
  848. /*--- function NumBlocksInDoc --------------------------------------------
  849.  *
  850.  *  Find the number of blocks in a document.
  851.  *
  852.  * Entry Doc points to a document.
  853.  *
  854.  * Exit  returns the number of textblocks in the document.
  855.  */
  856. int
  857. NumBlocksInDoc(Doc)
  858. TypDoc *Doc;
  859. {
  860.    TypBlock far *MyBlock;
  861.    HANDLE hMyBlock, hNewBlock;
  862.    int nBlocks = 0;
  863.  
  864.    if(!Doc) return(0);
  865.    hMyBlock = Doc->hFirstBlock;
  866.    do {
  867.       nBlocks++;
  868.       MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
  869.       hNewBlock = MyBlock->hNextBlock;
  870.       GlobalUnlock(hMyBlock);
  871.       hMyBlock = hNewBlock;
  872.    } while (hNewBlock);
  873.    
  874.    return(nBlocks);
  875. }
  876. /*--- function FindLineOrd ---------------------------------------------
  877.  *
  878.  *  Find the Nth line in a document.
  879.  *
  880.  *    Entry    Doc      points to a document.
  881.  *             LineOrd  is the ordinal of the line to find.
  882.  *                      0 = first line in document.
  883.  *
  884.  *    Exit     BlockPtr points to the block containing the line.
  885.  *             LinePtr  points to the line.
  886.  *             return value is TRUE iff we found the line.
  887.  */
  888. BOOL
  889. FindLineOrd(Doc,LineOrd,BlockPtr,LinePtr)
  890. TypDoc *Doc;
  891. unsigned int LineOrd;
  892. TypBlock far **BlockPtr;
  893. TypLine  far **LinePtr;
  894. {
  895.    unsigned int LinesSoFar = 0;
  896.    TypBlock far *MyBlockPtr;
  897.    TypLine  far *MyLinePtr;
  898.    HANDLE   hBlock,hNextBlock;
  899.    int retcode = 0;
  900.  
  901.    hBlock = Doc->hFirstBlock;
  902.    do {
  903.       MyBlockPtr = (TypBlock far *) GlobalLock(hBlock);
  904.       if(LinesSoFar+MyBlockPtr->NumLines > LineOrd) break;
  905.       LinesSoFar += MyBlockPtr->NumLines;
  906.       hNextBlock = MyBlockPtr->hNextBlock;
  907.       GlobalUnlock(hBlock);
  908.       hBlock = hNextBlock;
  909.    } while(hBlock);
  910.  
  911.    if(hBlock) {
  912.       MyLinePtr = (TypLine far *) ((char far *)MyBlockPtr + sizeof(TypBlock));
  913.       while(LinesSoFar < LineOrd) {
  914.          LinesSoFar++;
  915.          if(!NextLine(&MyBlockPtr,&MyLinePtr)) break;
  916.       }
  917.       retcode = TRUE;
  918.       *BlockPtr = MyBlockPtr;
  919.       *LinePtr  = MyLinePtr;
  920.    }
  921.    return(retcode);
  922. }
  923.  
  924. /*-- function MoveBytes -----------------------------------------------
  925.  *
  926.  *   Move a region of bytes in memory from one place to another.
  927.  *   Handle overlapping regions without destroying the source.
  928.  *
  929.  *    Entry    Source   points to the FWA of the source.
  930.  *             Target   points to the FWA of the target.
  931.  *             NumBytes is the number of bytes to copy (>= 0).
  932.  */
  933. void
  934. MoveBytes(FSource,FTarget,NumBytes)
  935. void far *FSource, far *FTarget;
  936. int NumBytes;
  937. {
  938.    char far *Source = FSource;
  939.    char far *Target = FTarget;
  940.  
  941.    if(Source < Target) {
  942.       Source += NumBytes - 1;
  943.       Target += NumBytes - 1;
  944.       while(NumBytes--) *(Target--) = *(Source--);
  945.    } else {
  946.       while(NumBytes--) *(Target++) = *(Source++);
  947.    }
  948. }
  949.  
  950. /*--- function InitDoc ---------------------------------------------------
  951.  *
  952.  *  Initialize the fields of a document.
  953.  */
  954. int
  955. InitDoc(Doc,hWnd,Parent,DType)
  956. TypDoc *Doc;
  957. HWND   hWnd;
  958. TypDoc *Parent;
  959. int DType;
  960. {
  961.    TypBlock far *BlockPtr;
  962.    HANDLE hBlock;
  963.  
  964.    Doc->hLastBlock     = 0;
  965.    Doc->TotalLines     = 0;
  966.    Doc->ActiveLines    = 0;
  967.    Doc->BlockSize      = BLOCK_SIZE;
  968.    Doc->SplitSize      = (BLOCK_SIZE*2)/3;
  969.    Doc->hDocWnd        = hWnd;
  970.    Doc->hLastSeenBlock = 0;
  971.    Doc->TopLineOrd     = 0;
  972.    Doc->ParentDoc      = Parent;
  973.    Doc->ParentLineID   = 0L;
  974.    Doc->SearchStr[0]   = '\0';
  975.    Doc->FindLineID     = 0L;
  976.    Doc->TopScLineID    = 0L;
  977.    Doc->InUse          = TRUE;
  978.    Doc->DocType        = DType;
  979.    Doc->hFindBlock     = 0;
  980.  
  981.    switch(DType) {
  982.       case DOCTYPE_NET:
  983.          Doc->OffsetToText = sizeof(TypLine) + sizeof(TypGroup);
  984.          break;
  985.       case DOCTYPE_GROUP:
  986.          Doc->OffsetToText = sizeof(TypLine) + sizeof(TypArticle);
  987.          break;
  988.       case DOCTYPE_ARTICLE:
  989.          Doc->OffsetToText = sizeof(TypLine);
  990.          break;
  991.    }
  992.  
  993.    hBlock = GlobalAlloc(GMEM_MOVEABLE,(long)BLOCK_SIZE);
  994.    if(hBlock) {
  995.       BlockPtr = (TypBlock far *) GlobalLock(hBlock);
  996.       SetupEmptyBlock(BlockPtr,hBlock,0,0,Doc);
  997.  
  998.       Doc->hFirstBlock    = hBlock;
  999.       Doc->hLastBlock     = hBlock;
  1000.       Doc->hCurAddBlock   = hBlock;
  1001.       Doc->AddOffset      = sizeof(TypBlock);
  1002.       Doc->AddLineID      = 0L;
  1003.       Doc->hCurTopScBlock = hBlock;
  1004.       Doc->TopScOffset    = sizeof(TypBlock);
  1005.       Doc->TopScLineID    = 0L;
  1006.       Doc->LastSeenLineID = 0L;
  1007.  
  1008.       GlobalUnlock(hBlock);
  1009.    } else {
  1010.       MessageBox(hWnd,"Could not allocate textblock","Out of Memory Error",MB_OK|MB_ICONHAND);
  1011.    }
  1012.  
  1013.    return(0);
  1014. }
  1015.  
  1016. /*-- function FreeDoc ----------------------------------------------
  1017.  *
  1018.  *  Free up all the text blocks associated with a document.
  1019.  *
  1020.  *    Entry    Doc   points to the document in question.
  1021.  */
  1022. void
  1023. FreeDoc(Doc)
  1024. TypDoc *Doc;
  1025. {
  1026.    TypBlock far *BlockPtr;
  1027.    HANDLE hBlock, hNextBlock;
  1028.  
  1029.    /* Start at the first block of the document, and travel           */
  1030.    /* down the linked list of blocks, freeing them.                  */
  1031.  
  1032.    hBlock = Doc->hFirstBlock;
  1033.    while(hBlock) {
  1034.       BlockPtr = (TypBlock far *) GlobalLock(hBlock);
  1035.       hNextBlock = BlockPtr->hNextBlock;
  1036.       GlobalUnlock(hBlock);
  1037.       GlobalFree(hBlock);
  1038.       hBlock = hNextBlock;
  1039.    }
  1040. }
  1041.  
  1042. /*--- function ForAllLines ---------------------------------------------
  1043.  *
  1044.  *  Perform an operation for all lines in a document.  The operation
  1045.  *  to be performed is specified by a C function argument.
  1046.  *
  1047.  *    Entry Doc            is the document.
  1048.  *          lpfnFunc       is a pointer to the function to call for
  1049.  *                         each line.
  1050.  *          lFlag          is a flag that's passed to the function.
  1051.  *
  1052.  */
  1053. void
  1054. ForAllLines(Doc,lpfnFunc,wFlag,wValue)
  1055. TypDoc *Doc;
  1056. void lpfnFunc(TypDoc *Doc, TypBlock far **BlockPtr, TypLine far **LinePtr, int wFlag, int wValue);
  1057. int wFlag;
  1058. int wValue;
  1059. {
  1060.    TypBlock far *BlockPtr;
  1061.    TypLine  far *LinePtr;
  1062.    TypLineID  old_lineID;
  1063.    BOOL looping=TRUE;
  1064.  
  1065.  
  1066.    TopOfDoc(Doc,&BlockPtr,&LinePtr);
  1067.    if(LinePtr->length != END_OF_BLOCK) {
  1068.       do {
  1069.          old_lineID = LinePtr->LineID;
  1070.          lpfnFunc(Doc,&BlockPtr,&LinePtr,wFlag,wValue);
  1071.          if(old_lineID == LinePtr->LineID &&
  1072.            LinePtr->length != END_OF_BLOCK) {
  1073.             looping = NextLine(&BlockPtr,&LinePtr);
  1074.          } else if(LinePtr->length == END_OF_BLOCK) {
  1075.             looping = FALSE;
  1076.          }
  1077.       } while(looping);
  1078.    }
  1079.    UnlockLine(BlockPtr,LinePtr,
  1080.     &(FindDoc->hFindBlock),&(FindDoc->FindOffset),&(FindDoc->FindLineID));
  1081.  
  1082. }
  1083.  
  1084.  
  1085. /*--- function FindString ----------------------------------------------
  1086.  *
  1087.  *   Locate a search string in a document.
  1088.  *
  1089.  *   Entry  StartAtTop        is TRUE iff we should start the search at
  1090.  *                            the top of the document.
  1091.  *          FindDoc           points to the document in which we are searching.
  1092.  *          ->hFindBlock      is the block to start at, if StartAtTop
  1093.  *                            is FALSE.
  1094.  *          ->FindOffset      is the offset within the block of the
  1095.  *                            line to start at, if StartAtTop is FALSE.
  1096.  *          ->SearchStr       has the string to search for.
  1097.  *
  1098.  *   Exit   returns -1 if the string was not found,
  1099.  *            else the offset of the string from the beginning of the line.
  1100.  *          FindDoc ...
  1101.  *          ->hFindBlock      has the block handle of the line which
  1102.  *                            was found (if any)
  1103.  *          ->FindOffset      has the offset of the found line (if any)
  1104.  *
  1105.  */
  1106. int
  1107. FindString(StartAtTop)
  1108. BOOL StartAtTop;
  1109. {
  1110.    TypBlock far *BlockPtr;
  1111.    TypLine  far *LinePtr;
  1112.    HANDLE  hBlock;
  1113.    unsigned int Offset;
  1114.    unsigned int TextOffset;
  1115.    BOOL found = -1;
  1116.    int TargLen=0;
  1117.    int SourceLen;
  1118.    char *Target;
  1119.    char sourceline[MAXINTERNALLINE];
  1120.    char targline[MAXFINDSTRING];
  1121.    char *sourceptr, far *orglineptr;
  1122.    char *targptr;
  1123.    TypLineID MyLineID;
  1124.    register char ch;
  1125.  
  1126.    if(StartAtTop) {
  1127.       hBlock = FindDoc->hFirstBlock;
  1128.       Offset = sizeof(TypBlock);
  1129.       MyLineID = 0L;
  1130.    } else {
  1131.       hBlock = FindDoc->hFindBlock;
  1132.       Offset = FindDoc->FindOffset;
  1133.       MyLineID = FindDoc->FindLineID;
  1134.    }
  1135.    LockLine(hBlock,Offset,MyLineID,&BlockPtr,&LinePtr);
  1136.    /* If doing a Find Next, skip forward one line before starting    */
  1137.    /* the search.                                                    */
  1138.    if(!StartAtTop) {
  1139.       NextLine(&BlockPtr,&LinePtr);
  1140.    }
  1141.  
  1142.    TextOffset = FindDoc->OffsetToText;
  1143.  
  1144.    Target = FindDoc->SearchStr;
  1145.    for(targptr=targline; ch = *Target, *(targptr++)=tolower(ch);
  1146.     TargLen++) Target++;
  1147.  
  1148.    if(LinePtr->length != END_OF_BLOCK) {
  1149.       do {
  1150.          orglineptr=((char far *)LinePtr)+TextOffset;
  1151.          sourceptr=sourceline;
  1152.          for(SourceLen=0; ch = *(orglineptr),
  1153.           *(sourceptr++)=tolower(ch); SourceLen++) orglineptr++;
  1154.          found = SearchLine(sourceline,SourceLen,targline,TargLen);
  1155.       } while(found == -1 && NextLine(&BlockPtr,&LinePtr));
  1156.    }
  1157.    UnlockLine(BlockPtr,LinePtr,
  1158.     &(FindDoc->hFindBlock),&(FindDoc->FindOffset),&(FindDoc->FindLineID));
  1159.    return(found);
  1160. }
  1161.  
  1162. /*--- function SearchLine -----------------------------------------------
  1163.  *
  1164.  *  Search a line for a target string.
  1165.  *
  1166.  *  Entry   Line     is a zero-terminated string to search.
  1167.  *          LineLen  is strlen(Line).  Redundant, but passed for
  1168.  *                   efficiency.
  1169.  *          Target   is the Target string to search for, zero-terminated.
  1170.  *          TargLen  is strlen(Target), passed for efficiency.
  1171.  *
  1172.  *  Exit    returns  -1 if not found, else the character position
  1173.  *                   in which the string was found (0=first).
  1174.  */
  1175. int
  1176. SearchLine(Line,LineLen,Target,TargLen)
  1177. char *Line;
  1178. int LineLen;
  1179. char *Target;
  1180. int TargLen;
  1181. {
  1182.    char *stopptr = Line + LineLen - TargLen + 1;
  1183.    char *lineptr;
  1184.    char *searchptr;
  1185.    char *targptr;
  1186.  
  1187.    if(LineLen<=0 || TargLen<=0 || TargLen > LineLen) return(-1);
  1188.  
  1189.    for(lineptr=Line; lineptr != stopptr; lineptr++) {
  1190.       searchptr = lineptr;
  1191.       for(targptr = Target; *targptr && *(targptr) == *(searchptr) ;) {
  1192.          targptr++;
  1193.          searchptr++;
  1194.       }
  1195.       if(!(*targptr)) {
  1196.          return(lineptr - Line);
  1197.       }
  1198.    }
  1199.    return(-1);
  1200. }
  1201.  
  1202. /*--- function DoFind ---------------------------------------------------
  1203.  *
  1204.  *  Controlling routine for searching for text.
  1205.  *  Takes care of displaying window properly when search is done.
  1206.  *
  1207.  *    Entry    StartAtTop  is TRUE iff we should start at the top
  1208.  *                         of the document.
  1209.  *             FindDoc     points to the document being searched.
  1210.  *                         All the info we need is in fields in this doc.
  1211.  */
  1212. BOOL
  1213. DoFind(StartAtTop)
  1214. BOOL StartAtTop;
  1215. {
  1216.    int CharPos;
  1217.    int iline;
  1218.    int X,Y;
  1219.    HDC hDC;
  1220.    TypBlock far *BlockPtr;
  1221.    TypLine far *LinePtr;
  1222.    HANDLE hBlock;
  1223.    unsigned int Offset;
  1224.    char far *textptr;
  1225.    POINT point;
  1226.    int mylen;
  1227.    int found = FALSE;
  1228.    BOOL refresh = FALSE;
  1229.    int goline;
  1230.    unsigned int LineOrd;
  1231.    unsigned int LastAllowedLineOrd;
  1232.    TypLineID MyLineID;
  1233.  
  1234.    CharPos = FindString(StartAtTop);
  1235.    if(CharPos >= 0) {
  1236.       iline = LineOnScreen(FindDoc,FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID);
  1237.       /* If this line wasn't on the screen, we are going to have
  1238.        * to adjust the top of the screen.
  1239.        * Make the found line the top of the screen, then back up a
  1240.        * little to give the user a context in which to view the line.
  1241.        */
  1242.       if(iline == -1) {
  1243.          FindDoc->hCurTopScBlock = FindDoc->hFindBlock;
  1244.          FindDoc->TopScOffset = FindDoc->FindOffset;
  1245.          FindDoc->TopScLineID = FindDoc->FindLineID;
  1246.          refresh = TRUE;
  1247.          LockLine(FindDoc->hCurTopScBlock,FindDoc->TopScOffset,FindDoc->TopScLineID,
  1248.           &BlockPtr,&LinePtr);
  1249.          for(goline=FindDoc->ScYLines/4; goline; goline--) {
  1250.             PrevLine(&BlockPtr,&LinePtr);
  1251.          }
  1252.          /* Have we gone past the top of the last screen?
  1253.           * If so, move the top line back to the top of the last screen.
  1254.           */
  1255.          LineOrd = WhatLine(BlockPtr,LinePtr);
  1256.          LastAllowedLineOrd = FindDoc->TotalLines - FindDoc->ScYLines;
  1257.          if(LineOrd > LastAllowedLineOrd) {
  1258.             GlobalUnlock(BlockPtr->hCurBlock);
  1259.             FindLineOrd(FindDoc,LastAllowedLineOrd,&BlockPtr,&LinePtr);
  1260.             LineOrd = LastAllowedLineOrd;
  1261.          }
  1262.          UnlockLine(BlockPtr,LinePtr,&(FindDoc->hCurTopScBlock),
  1263.           &(FindDoc->TopScOffset),&(FindDoc->TopScLineID));
  1264.          FindDoc->TopLineOrd = LineOrd;
  1265.          iline = LineOnScreen(FindDoc,FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID);
  1266.       }
  1267. #if 0
  1268.  
  1269.       LockLine(FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID,
  1270.        &BlockPtr,&LinePtr);
  1271.       textptr = (char far *) LinePtr + FindDoc->OffsetToText;
  1272.       hDC = GetDC(FindDoc->hDocWnd);
  1273.       SelectObject(hDC,hFont);
  1274.       point.x = SideSpace + LOWORD(GetTextExtent(hDC,textptr,CharPos));
  1275.       ReleaseDC(FindDoc->hDocWnd,hDC);
  1276.       UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  1277.  
  1278.       point.y = TopSpace + iline * LineHeight + 4*LineHeight/5;
  1279.       ClientToScreen(FindDoc->hDocWnd,&point);
  1280.       SetCursorPos(point.x,point.y);
  1281. #endif
  1282.  
  1283.       InvalidateRect(FindDoc->hDocWnd,NULL,FALSE);
  1284.       found = TRUE;
  1285.    }
  1286.    return(found);
  1287. }
  1288.